from dataclasses import dataclass
import pandas as pd
from .window import *
from .datagrad import Data2Grade
from collections import defaultdict
import itertools
import math


class LoanUrgency:
    
    def __init__(
        self,
        applist: [{},{},...],
        data: pd.DataFrame,
        cutwin: TimeWindow = TimeWindow([3, 7, 15, 30, 60, 90, 180, 50000])):
        """
        app_list: 用户app原始安装列表数据
        data: 外部数据关联数据表，如竞品列表
        cutwin: TimeWindow类，用于生成时间窗口，拆分原始数据
        """
        self.app_list = pd.DataFrame(applist)
        self.data = data
        self.cutwin = cutwin
        self.loan_list = data["package_name"].tolist()
        self.get_meta()

    def get_meta(self):
        self.meta = {
            "loannum_oneday": "用户单日下载竞品app最大值、比例",
            "longest_continuedays": "用户下载竞品app最大连续天数、周期",
            "get_days_from_now": "用户最早下载的竞品app距今天数",
            "special_name_feature":"package_name 包含特殊字符 如ltd或company",
            "get_topapp_score":"此用户安装次数最多的app的google分数、安装量"
        }
        self.func_map = {
            "loannum_oneday": self.loannum_oneday,
            "longest_continuedays": self.longest_continuedays,
            "get_days_from_now": self.get_days_from_now,
            "special_name_feature":self.special_name_feature,
            "get_topapp_score":self.get_topapp_score
        }

    def __call__(self, fuc_list: list):
        dfs = []
        for name, al in self.cutwin.iter(self.app_list):
            if not al.empty:
                for func in fuc_list:
                    feature_dict = self.func_map[func](
                        data=al, loan_data=self.deal_data(al, self.data)
                    )
                    if feature_dict:
                        features_new = [
                            {f"{key}_{name}": feature_dict[key]} for key in feature_dict
                        ]
                        dfs.extend(features_new)

        return self.cutwin.merge_dfs(dfs)

    def loannum_oneday(self, loan_data: pd.DataFrame, **kwargs):
        feature_dict = defaultdict(int)
        if not loan_data.empty:
            loan_data['inst_date'] = loan_data["inst_time"].apply(lambda x:str(x)[:10])
            download_oneday = (
                loan_data.groupby("inst_date")["app"]
                .count()
                .reset_index()
                .sort_values("app", ascending=False)
                .reset_index(drop=True)
            )
            feature_dict[self.join_str(["downlmax", "cnt"])] = download_oneday[
                "app"
            ].values[0]
            feature_dict[self.join_str(["downlmax", "bydayrate"])] = (
                download_oneday["app"].values[0] / loan_data.shape[0]
            )

        return feature_dict

    def longest_continuedays(self, data: pd.DataFrame, **kwargs):
        feature_dict = defaultdict(int)

        def _is_credit(name):
            if name in self.loan_list:
                return "credit"
            else:
                return "app"

        if not data.empty:
            data['inst_date'] = data["inst_time"].apply(lambda x:str(x)[:10])
            data["category"] = data["package"].apply(_is_credit)
            loan_data = data[["inst_date", "category"]]
            feature_dict[
                self.join_str(["conti", "cnt"])
            ] = self.get_longestcnt_download(loan_data)
            feature_dict[
                self.join_str(["contiday", "cnt"])
            ] = self.get_longestdays_download(loan_data)

        return feature_dict

    def get_days_from_now(self, loan_data: pd.DataFrame, **kwargs):
        feature_dict = defaultdict(int)
        feature_dict[self.join_str(["maxinst", "dayfn"])] = loan_data["up2in_time_diff_days"].max()
        feature_dict[self.join_str(["mininst", "dayfn"])] = loan_data["up2in_time_diff_days"].min()
        feature_dict[self.join_str(["suminst", "dayfn"])] = loan_data["up2in_time_diff_days"].sum()
        feature_dict[self.join_str(["meaninst", "dayfn"])] = feature_dict[self.join_str(["suminst", "dayfn"])]/len(loan_data["up2in_time_diff_days"])

        return feature_dict

    def get_topapp_score(self, loan_data: pd.DataFrame, **kwargs):
        """此用户安装次数最多的app的google分数、安装量"""
        feature_dict = defaultdict(int)
        if not loan_data.empty:
            top_app = (
                loan_data.groupby("appName")["package"]
                .count()
                .reset_index()
                .sort_values("package", ascending=False)
                .reset_index(drop=True)["package"]
            )["appName"].values[0]
            feature_dict[self.join_str(["topapp", "scr"])] = self.data[
                self.data["app_name"] == top_app
            ]["score_old"].values[0]
            feature_dict[self.join_str(["topapp", "inst"])] = self.data[
                self.data["app_name"] == top_app
            ]["installs_old"].values[0]

        return feature_dict
    
    def special_name_feature(self, loan_data:pd.DataFrame, **kwargs):
        """package_name 包含特殊字符 如ltd或company"""
        feature_dict = defaultdict(int)
        if not loan_data.empty:
            for pck in loan_data['package']:
                if ("ltd" in pck) or ("company" in pck):
                    feature_dict[self.join_str(["specn", "cnt"])] += 1
        
        return feature_dict

    def get_longestcnt_download(self, df: pd.DataFrame):
        # Convert 'inst_date' column to datetime
        df["inst_date"] = pd.to_datetime(df["inst_date"])

        # date need sort_values
        df = df.sort_values(by="inst_date").reset_index(drop=True)

        # Find consecutive days of downloading the credit category app
        df["consecutive_group"] = (df["category"] != df["category"].shift(1)).cumsum()
        consecutive_counts = (
            df[df["category"] == "credit"].groupby("consecutive_group").size()
        )

        # Find the longest consecutive days
        longest_consecutive_days = consecutive_counts.max()
        return longest_consecutive_days

    def get_longestdays_download(self, df: pd.DataFrame):
        # Convert 'date' column to datetime
        df["inst_date"] = pd.to_datetime(df["inst_date"])

        # date need sort_values
        df = df.sort_values(by="inst_date").reset_index(drop=True)

        # Find consecutive days of downloading the credit category app with one day in between
        df["difference"] = df["inst_date"] - df["inst_date"].shift(1)
        df["consecutive_group"] = (df["difference"] > pd.Timedelta("1 days")).cumsum()
        consecutive_counts = (
            df[df["category"] == "credit"].groupby("consecutive_group").size()
        )

        # Find the longest consecutive days
        longest_consecutive_days = consecutive_counts.max()
        return longest_consecutive_days

    def deal_data(self, applist: pd.DataFrame, data: pd.DataFrame):
        return applist[applist["package"].isin(self.loan_list)]

    def join_str(self, tag_list: list):
        return "_".join(tag_list)
